<?php
class Pcs
{
	protected $appKey;
	protected $secretKey;
	protected $maxsize;
	public $basepath;
	protected $errors;

	function __construct()
	{
		$this->maxsize = 1024 * 1024 * 4;
		$this->appKey = get_option('baidupan_appkey');
		$this->secretKey = get_option('baidupan_secretkey');
		$appname = get_option('baidupan_appname');
		$domain = get_option('siteurl');
		$domain = parse_url($domain);
		$domain = $domain['host'];
		$this->basepath = '/apps/' . $appname . '/' . $domain;
		$this->errors = [];
	}

	/**
	 * 添加错误
	 */
	protected function setError($message)
	{
		$this->errors[] = $message;
	}

	/**
	 * 获取错误
	 */
	public function getError()
	{
		return join('<br>', $this->errors);
	}

	/**
	 * 文件大小转换
	 */
	protected function numberToSize($num)
	{
		if ($num >= 1073741824 * 1024) {
			$size = round($num / 1073741824 / 1024, 2) . ' TB';
		} elseif ($num >= 1073741824) {
			$size = round($num / 1073741824, 2) . ' GB';
		} elseif ($num >= 1048576) {
			$size = round($num / 1048576, 2) . ' MB';
		} elseif ($num >= 1024) {
			$size = round($num / 1024, 2) . ' KB';
		} else {
			$size = $num . ' B';
		}
		return $size;
	}

	/**
	 * 获取Token
	 */
	public function accessToken($code = '')
	{
		$url = 'https://openapi.baidu.com/oauth/2.0/token';
		$grant_type = $code ? 'authorization_code' : 'refresh_token';
		$params = [
			'grant_type' => $grant_type,
			'client_id' => $this->appKey,
			'client_secret' => $this->secretKey
		];

		if ($grant_type == 'authorization_code') {
			$params['code'] = $code;
			$params['redirect_uri'] = admin_url('admin.php?page=baidupan-setting');
		} else {
			$params['refresh_token'] = get_option('baidupan_refresh_token');
		}

		$response = wp_remote_post($url, ['body' => $params]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);
		if (isset($data->access_token)) {
			update_option('baidupan_access_token', $data->access_token);
			update_option('baidupan_expires_in', current_time('timestamp') + $data->expires_in);
			update_option('baidupan_refresh_token', $data->refresh_token);
			return $data->access_token;
		}

		$this->setError($response['body']);
	}

	/**
	 * 刷新Token
	 */
	public function getToken()
	{
		$expires_in = get_option('baidupan_expires_in');
		if (!$expires_in || (current_time('timestamp') - 30) > $expires_in) {
			return $this->accessToken();
		} else {
			return get_option('baidupan_access_token');
		}
	}

	/**
	 * 获取网盘容量
	 */
	public function getQuota()
	{
		$url = 'https://pan.baidu.com/api/quota';
		$access_token = $this->getToken();
		$params = [
			'access_token' => $access_token,
			'checkfree' => 1,
		];

		$response = wp_remote_post($url, ['body' => $params]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return;
		}

		$data = json_decode($response['body']);

		if ($data && $data->errno == 0) {
			return [
				'total' => $this->numberToSize($data->total),
				'free' => $this->numberToSize($data->free),
				'used' => $this->numberToSize($data->used)
			];
		}
		$this->setError($response['body']);
	}

	/**
	 * 获取用户信息
	 */
	public function getUserInfo()
	{
		$url = 'https://pan.baidu.com/rest/2.0/xpan/nas?method=uinfo';
		$access_token = $this->getToken();
		$params = [
			'access_token' => $access_token,
		];

		$response = wp_remote_post($url, ['body' => $params]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return;
		}

		$data = json_decode($response['body']);

		if ($data && $data->errno == 0) {
			return [
				'baidu_name' => $data->baidu_name,
				'netdisk_name' => $data->netdisk_name,
				'avatar_url' => $data->avatar_url,
				'vip_type' => $data->vip_type,
				'uk' => $data->uk
			];
		}
		$this->setError($response['body']);
	}

	/**
	 * 文件切片
	 */
	protected function fileSlicing($file)
	{
		$file = fopen($file, 'rb');
		$arr = [];
		$chunk_size = $this->maxsize;
		$tmpdir = trim(sys_get_temp_dir(), '/');
		while (!feof($file)) {
			$slice = fread($file, $chunk_size);
			$md5 = md5($slice);
			$tmp = $tmpdir . '/' . $md5;
			@file_put_contents($tmp, $slice);
			chmod($tmp, 0644);
			$arr[] = [
				'file' => $tmp,
				'md5' => $md5,
			];
		}
		fclose($file);
		return $arr;
	}

	/**
	 * 预上传
	 * 秒传返回文件信息，否则返回任务id
	 */
	protected function preCreate($path, $size, $block_list, $md5file, $slicemd5)
	{
		$access_token = $this->getToken();
		$url = 'https://pan.baidu.com/rest/2.0/xpan/file?method=precreate';
		$params = [
			'access_token' => $access_token,
			'path' => $path,
			'size' => $size,
			'rtype' => 3,
			'isdir' => 0,
			'autoinit' => 1,
			'block_list' => $block_list,
			'content-md5' => $md5file,
			'slice-md5' => $slicemd5
		];

		$response = wp_remote_post($url, ['body' => $params]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);

		if ($data) {
			if (isset($data->return_type) && 2 == $data->return_type) {
				return [
					'fs_id' => $data->info->fs_id,
					'md5' => $data->info->md5,
					'path' => $data->info->path,
					'server_filename' => basename($data->info->path),
					'size' => $data->info->size,
					'ctime' => $data->info->ctime,
					'mtime' => $data->info->mtime,
				];
			} elseif (isset($data->uploadid)) {
				return $data->uploadid;
			}
		}

		$this->setError($response['body']);
	}

	/**
	 * 分片上传
	 */
	protected function uploadSlice($file, $path, $uploadid, $partseq = 0)
	{
		global $wp_version;
		$access_token = $this->getToken();
		$url = 'https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?method=upload';
		//上传需要将参数拼接在URL里
		$url .= "&access_token={$access_token}&type=tmpfile&path={$path}&uploadid={$uploadid}&partseq={$partseq}";
		$curlfile = new \CurlFile($file);
		$http = new WP_Http_Curl;
		$params = array(
			// 'access_token' => $access_token,
			// 'type' => 'tmpfile',
			// 'path' => $path,
			// 'uploadid' => $uploadid,
			// 'partseq' => $partseq,
			'file' => $curlfile
		);

		$response = $http->request($url, [
			'method' => 'POST',
			'sslverify' => true,
			'sslcertificates' => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
			'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url('/'),
			'stream' => false,
			'decompress' => false,
			'timeout' => 600,
			'filename' => basename($file),
			'body' => $params
		]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);
		if ($data && isset($data->md5)) {
			return $data->md5;
		}
		$this->setError(json_encode($response));
	}

	/**
	 * 合并分片
	 */
	protected function createFile($path, $size, $uploadid, $block_list)
	{
		$access_token = $this->getToken();
		$url = 'https://pan.baidu.com/rest/2.0/xpan/file?method=create';
		$params = [
			'access_token' => $access_token,
			'path' => $path,
			'size' => $size,
			'isdir' => 0,
			'rtype' => 3, //0返回冲突，1重命名，2path冲突且block_list不同才重命名，3覆盖
			'uploadid' => $uploadid,
			'block_list' => $block_list
		];

		$response = wp_remote_post($url, ['body' => $params]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);
		if ($data && $data->errno == 0) {
			return [
				'fs_id' => $data->fs_id,
				'md5' => $data->md5,
				'path' => $data->path,
				'server_filename' => basename($data->path),
				'size' => $data->size,
				'ctime' => $data->ctime,
				'mtime' => $data->mtime,
			];
		}

		$this->setError($response['body']);
	}

	/**
	 * 小于4M文件的上传
	 */
	protected function uploadFile($file, $path, $uploadid)
	{
		global $wp_version;
		$access_token = $this->getToken();
		$url = 'https://pan.baidu.com/rest/2.0/xpan/file?method=upload';
		//上传需要将参数拼接在URL里
		$url .= "&access_token={$access_token}&path={$path}&uploadid={$uploadid}";
		$curlfile = new \CurlFile($file);
		$http = new WP_Http_Curl;
		$params = array(
			// 'access_token' => $access_token,
			// 'path' => $path,
			// 'uploadid' => $uploadid,
			'file' => $curlfile
		);

		$response = $http->request($url, [
			'method' => 'POST',
			'sslcertificates' => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
			'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url('/'),
			'stream' => false,
			'decompress' => false,
			'timeout' => 600,
			'filename' => basename($file),
			'body' => $params
		]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);
		if ($data && isset($data->md5)) {
			return [
				'fs_id' => $data->fs_id,
				'md5' => $data->md5,
				'path' => $data->path,
				'server_filename' => basename($data->path),
				'size' => $data->size,
				'ctime' => $data->ctime,
				'mtime' => $data->mtime,
			];
		}

		$this->setError($response['body']);
	}

	/**
	 * 上传文件
	 */
	public function upload($file, $target = '')
	{
		$target = $target ? $target : $file;
		$path = $this->basepath . '/' . trim($target, '/');
		$size = filesize($file);
		$md5file = md5_file($file);

		//前256K MD5校验实现秒传
		$handle = fopen($file, 'rb');
		$slice256 = fread($handle, 256 * 1024);
		fclose($handle);
		$slicemd5 = md5($slice256);

		//分片MD5必须传递
		if ($size > $this->maxsize) {
			$slices = $this->fileSlicing($file);
			$block_list = array_column($slices, 'md5');
		} else {
			$block_list = [$md5file];
		}
		$block_list = json_encode($block_list);

		$uploadid = $this->preCreate($path, $size, $block_list, $md5file, $slicemd5);

		//秒传返回文件信息
		if (is_array($uploadid)) {
			return $uploadid;
		}

		//预上传失败
		if (!$uploadid) {
			return false;
		}

		//小于4MB不能分片上传
		if ($size <= $this->maxsize) {
			return $this->uploadFile($file, $path, $uploadid);
		}

		foreach ($slices as $partseq => $slice) {
			if (!$this->errors) {
				$upslice = $this->uploadSlice($slice['file'], $path, $uploadid, $partseq);
				if (!$upslice || $upslice != $slice['md5']) {
					$this->setError('切片上传失败，或切片MD5校验失败');
				}
			}
			if (file_exists($slice['file'])) {
				unlink($slice['file']);
			}
		}

		if (!$this->errors) {
			return json_encode($this->createFile($path, $size, $uploadid, $block_list));
		}
	}

	/**
	 * 获取文件列表
	 */
	public function getList($dir = '', $start = 0, $limit = 10000, $folder = 0)
	{
		$dir = $this->basepath . '/' . trim($dir, '/');
		$url = 'https://pan.baidu.com/rest/2.0/xpan/file?method=list';
		$access_token = $this->getToken();
		$params = [
			'access_token' => $access_token,
			'dir' => $dir,
			'order' => 'time',
			'desc' => 1,
			'start' => $start,
			'limit' => $limit,
			'folder' => $folder, //0 返回所有，1 只返回目录
		];
		$response = wp_remote_post($url, ['body' => $params]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);
		if ($data && $data->errno == 0) {
			return $data->list;
		}

		$this->setError($response['body']);
	}

	/**
	 * 下载文件
	 * @param string $type url：返回百度下载地址；origin：返回百度源文件地址；stream：返回文件流和文件名
	 */
	public function download($file_id, $type = 'url')
	{
		$url = 'https://pan.baidu.com/rest/2.0/xpan/multimedia?method=filemetas';
		$access_token = $this->getToken();
		$params = [
			'access_token' => $access_token,
			'fsids' => "[$file_id]",
			'dlink' => 1
		];

		$response = wp_remote_post($url, ['body' => $params]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);
		if ($data && $data->errno == 0) {
			$download_url = $data['list'][0]['dlink'] . '&access_token=' . $access_token;
			if ($type == 'url') return $download_url;

			$res = wp_remote_get($download_url);
			$origin = str_ireplace(["location:", "\r\n", "\r", "\n", " "], '', wp_remote_retrieve_header($response, 'Location'));

			if ($type == 'origin') return $origin;

			$fp = @fopen($origin, 'rb');
			if (!$fp) {
				return false;
			}
			$file_content = stream_get_contents($fp);
			fclose($fp);

			if ($type == 'stream') {
				return [
					'filename' => $data['list'][0]['filename'],
					'size' => $data['list'][0]['size'],
					'stream' => $file_content,
				];
			}
		}

		$this->setError($response['body']);
	}

	/**
	 * 删除文件
	 */

	public function delete($files)
	{
		$url = 'https://pan.baidu.com/rest/2.0/xpan/file?method=filemanager';
		$access_token = $this->getToken();
		foreach ($files as $key => $file) {
			$files[$key] = $this->basepath . '/' . trim($file, '/');
		}
		$params = [
			'access_token' => $access_token,
			'opera' => 'delete',
			'async' => 1,
			'filelist' => $files
		];

		$response = wp_remote_post($url, [
			'timeout' => 600,
			'body' => $params
		]);

		if (is_wp_error($response)) {
			$this->setError($response->get_error_message());
			return false;
		}

		$data = json_decode($response['body']);
		if ($data && $data->errno == 0) {
			return true;
		}
		$this->setError($response['body']);
	}
}
